/*
 *  Copyright 2011 Iurii Dunko, Iana Potochniak.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */

package org.opu.yyminijava;

import org.opu.yyminijava.codegeneration.ClassRepository;
import org.opu.yyminijava.symboltable.SymbolTable;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import org.antlr.runtime.ANTLRFileStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.opu.yyminijava.codegeneration.CodeGenerationHandler;
import org.opu.yyminijava.syntaxtree.Goal;
import org.opu.yyminijava.util.ArgsParserr;
import org.opu.yyminijava.visitor.PrintHandler;
import org.opu.yyminijava.visitor.SymbolTableBuildHandler;
import org.opu.yyminijava.visitor.TypeCheckHandler;
import org.opu.yyminijava.visitor.Visitor;

/**
 *
 * @author Iurii Dunko
 * @author Iana Potochniak
 */
public class Main {

    public static void main(String[] args) throws Exception {
    	ArgsParserr ap = new ArgsParserr(args);
        String fname = ap.getArg(0);
		if (fname == null || ap.contains('h') /*|| ap.conatinsBoth('c', 'e')*/){
            helpAndExit();
        }
        
//        if (ap.contains('c')){
    		compile(fname, ap.contains('s'), ap.contains('p'));
//        } else if (ap.contains('e')){
//        	execute(fname);
//        } else {
//        	helpAndExit();
//        }
    }

    /**
     * @param name name of the input file
     * @param ps print symbol table or not
     * @param pb print bytecode or not
     * @throws IOException
     * @throws RecognitionException
     */
	public static void compile(String name, boolean ps, boolean pb) 
			throws IOException, RecognitionException {
		compile(name, name.substring(0, name.length() - 5) + ".tjc", ps, pb);
	}
	/**
     * @param in name of the input file
     * @param out name of the output file
     * @param ps print symbol table or not
     * @param pb print bytecode or not
     * @throws IOException
     * @throws RecognitionException
     */
	@SuppressWarnings("unchecked")
	public static void compile(String in, String out, boolean ps, boolean pb) 
			throws IOException, RecognitionException {
		yyLexer lexer = new yyLexer(new ANTLRFileStream(in));
        CommonTokenStream tokens = new CommonTokenStream(lexer);

        yyParser parser = new yyParser(tokens);
        Goal root = parser.goal();

        SymbolTable symbolTable = new SymbolTable();
        Visitor visitor = new Visitor();
        visitor.addHandler(new SymbolTableBuildHandler(symbolTable));
        root.accept(visitor);

        symbolTable.resetTable();
        
        if (ps) {
			System.out.println("Symbol table");
			System.out.println("{");
			symbolTable.printTable();
			System.out.println("}");
			System.out.println();
		}
        
		TypeCheckHandler tch = new TypeCheckHandler(symbolTable);
        CodeGenerationHandler cgh = new CodeGenerationHandler(symbolTable);
        visitor = new Visitor();
        visitor.addHandler(new SymbolTableBuildHandler(symbolTable, false));
        visitor.addHandler(tch);
        visitor.addHandler(cgh);
        root.accept(visitor);

        if (printErrors(in, tch.getProblemsStack(), cgh.getProblemsStack())){
            ClassRepository repository = cgh.getRepository();
            if(pb){
            	repository.print();
            }
            repository.write(new FileOutputStream(out));
        }
	}
	
	public static void execute(String name){
		throw new RuntimeException();
	}

	private static void helpAndExit() {
		System.out.println("Use: java -jar <application> [arguments] <grammar file> [arguments]");
		System.out.println("  <application>  - yy-minijava-0.0.1-jar-with-dependencies.jar");
		System.out.println("  <grammar file> - input file");
		System.out.println("  arguments (unix like - '-sp' = '-s -p'):");
//		System.out.println("    -e  execute input file");
//		System.out.println("    -c  compile input file");
		System.out.println("    -h  pring this help and exit");
		System.out.println("    -p  print bytecode at compile time");
		System.out.println("    -s  print symbol table at compile time");
		System.out.println();
//		System.out.println("You can't specify 'e' and 'c' option at the same moment.");
//		System.out.println("You must specify one of the options 'e' or 'c'.");
//		System.out.println();
		System.out.println("Example of compilation may be: ");
		System.out.println("  java -jar yy-minijava-0.0.1-jar-with-dependencies.jar -sp MyApp.java");
//		System.out.println();
//		System.out.println("Example of execution may be: ");
//		System.out.println("  java -jar yy-minijava-0.0.1-jar-with-dependencies.jar MyApp.tjc -e");
		System.exit(1);
	}

    private static boolean printErrors(String fName, List<? extends Throwable>... errs){
        for (List<? extends Throwable> list : errs) {
            if (!list.isEmpty()){
                System.out.println(fName + " errors :");
                for (int i = 0; i < list.size(); i++) {
                    System.out.println(list.get(i));
                }
                return false;
            }
        }
        return true;
    }
}
